#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
/*
label series:
+	0:int
+	1:char
+	2:while
+	3:if
+	4:else
+	5:break
+	6:return
+	7:=
+	8:!=
+	9:==
+	10:+
+	11:-
+	12:*
+	13:/
+	14:>
+	15:>=
+	16:<
+	17:<=
+	18:& &&
+	19:| ||
+	20:;
+	21:int_id
+	22:(
+	23:{
+	24:}
+	25:)
+	26:!
+	27:number
+	28:char_id
*/
char label_series[29][20] = {"int","char","while","if","else","break","return","=","!=","==","+","-","*",
							 "/",">",">=","<","<=","AND","OR",";","int_id","(","{","}",")","!","number","char_id"};
char CharId[100][20] = {0};//char ids, almost 99 items(1 to 99), almost 19 long
char IntId[100][20] = {0};//integer ids, almost 99 items(1 to 99), almost 19 long
typedef struct token
{//<lable,value>
	int label;
	int value;
}token;
typedef struct tokenlist
{//TokenStream
	token *list;
	long size;
	long length;
}tokenlist;
int CheckWord(char *Head)
{/*return label, unknown return -1*/
	if (!strcmp("int",Head))//Word == "int"
		return 0;
	else if (!strcmp("char",Head))//Word == "char"
		return 1;
	else if (!strcmp("while",Head))//Word == "while"
		return 2;
	else if (!strcmp("if",Head))//Word == "if"
		return 3;
	else if (!strcmp("else",Head))//Word == "else"
		return 4;
	else if (!strcmp("break",Head))//Word == "break"
		return 5;
	else if (!strcmp("return",Head))//Word == "return"
		return 6;
	else
		return -1;
}
int FindIntIdExists(char *IdStr)
{/*if found, return 1,else return index*/
	int i = 1;
	while (IntId[i][0])//int contains
	{
		if (!strcmp(IntId[i],IdStr))
			return i;//
	}
	return 0;
}
int FindCharIdExists(char *IdStr)
{/*if found, return 1,else return index*/
	int i = 1;
	while (CharId[i][0])//int contains
	{
		if (!strcmp(CharId[i],IdStr))
			return i;//
	}
	return 0;
}
int FillTokenList(tokenlist *ListStruct,int label,int value)
{//add one label to tokenlist, return 0
	if (ListStruct->length >= ListStruct->size-5)
	{
		ListStruct->list = (token*)realloc(ListStruct->list,(50+ListStruct->size)*sizeof(token));
		ListStruct->size += 50;
	}
	ListStruct->list[ListStruct->length].label = label;
	ListStruct->list[ListStruct->length].value = value;
	ListStruct->length++;

	return 0;
}
int SortAndFill(int wordlabel,tokenlist *list,char **pPtr,char forechar)
{/*Sort proper label of declarations and fill them, then change pSrc, error return -1*/
	char *cur = NULL;//cursor
	char temp;
	int i = 1;
	switch (wordlabel)
	{
	case 0://declare integer
		**pPtr = forechar;
		(*pPtr)++;//cursor move on
		cur = *pPtr;//fast cursor
		if (!isalpha(**pPtr))
		{
			printf("Wrong declaration\n");
			return -1;
		}
		else
		{
			cur++;//fast cursor moves on
			do
			{
				while(*cur != ',' && *cur != ';')//end of one id
					cur++;
				temp = *cur;
				*cur = 0;
				if (CheckWord(*pPtr) == -1)//not equal to reseverd id
				{
					while (IntId[i][0])//filled, so move on
						i++;
					FillTokenList(list,0,i);//<int,id>
					strcpy(IntId[i],*pPtr);
				}
				else
				{
					printf("Wrong declaration\n");
					return -1;
				}
				*cur = temp;
				if (temp == ',')
					cur++;
				*pPtr = cur;//trans to next id word
			}while(*cur != ';');//end of declaration
		}
		break;
	case 1://declare char
		**pPtr = forechar;
		(*pPtr)++;//cursor move on
		cur = *pPtr;//fast cursor
		if (!isalpha(**pPtr))
		{
			printf("Wrong declaration\n");
			return -1;
		}
		else
		{
			cur++;//fast cursor moves on
			do
			{
				while(*cur != ',' && *cur != ';')//end of one id
					cur++;
				temp = *cur;
				*cur = 0;
				if (CheckWord(*pPtr) == -1)//not equal to reseverd id
				{
					while (CharId[i][0])//filled, so move on
						i++;
					FillTokenList(list,1,i);//<char,id>
					strcpy(CharId[i],*pPtr);
				}
				else
				{
					printf("Wrong declaration\n");
					return -1;
				}
				*cur = temp;
				if (temp == ',')
					cur++;
				*pPtr = cur;//trans to next id word
			}while(*cur != ';');//end of declaration
		}
		break;
	default:
		break;
	}
}
tokenlist* Lexical_Analysis(char *SourceCode)
{/*succeed return tokenstream, fail return NULL*/
	tokenlist *tstream = NULL;
	char *pSrc = SourceCode;//fast chk pointer
	char temp;//temp buf
	int wordlabel,i;//temp
	tstream = (tokenlist*)malloc(sizeof(tokenlist));//init tokenlist
	if (!tstream)
		return NULL;//init failed
	tstream->list = (token*)malloc(50*sizeof(token));//init 50 tokens
	if (!tstream->list)
		return NULL;//init failed
	tstream->length = 0;
	tstream->size = 50;

	while(*pSrc)//to the end
	{
		switch (*pSrc)
		{
		case '/'://notes or '/'
			if (*(pSrc+1) == '*')//		/*notes*/
			{
				pSrc += 2;
				while (*pSrc != '*')
				{
					if (*pSrc == 0)//end of file
					{
						printf("Unpredictable end of the file.\n");
						return NULL;
					}
					if (*(pSrc+1) != '/')
					{
						pSrc++;//not end of notes, continue
					}
				}
				pSrc += 2;
			}
			else if (*(pSrc+1) == '/')//	//notes
			{
				while (*pSrc != '\n')//find end of line
					pSrc++;//not end of notes, continue
				pSrc++;
			}
			else//		/
			{
				FillTokenList(tstream,13,0);//add '/' to tokenlist
				pSrc++;
			}
			SourceCode = pSrc;
			break;
		case '='://== or =
			if (*(pSrc+1) == '=')//	==
			{
				FillTokenList(tstream,9,0);//add '==' to tokenlist
				pSrc += 2;
			}
			else
			{
				FillTokenList(tstream,7,0);//add '=' to tokenlist
				pSrc++;
			}
			SourceCode = pSrc;
			break;
		case '!'://! or !=
			if (*(pSrc+1) == '=')//	!=
			{
				FillTokenList(tstream,8,0);//add '!=' to tokenlist
				pSrc += 2;
			}
			else
			{
				FillTokenList(tstream,26,0);//add '!' to tokenlist
				pSrc++;
			}
			SourceCode = pSrc;
			break;
		case '>'://> or >=
			if (*(pSrc+1) == '=')//	>=
			{
				FillTokenList(tstream,15,0);//add '>=' to tokenlist
				pSrc += 2;
			}
			else
			{
				FillTokenList(tstream,14,0);//add '>' to tokenlist
				pSrc++;
			}
			SourceCode = pSrc;
			break;
		case '<'://< or <=
			if (*(pSrc+1) == '=')//	<=
			{
				FillTokenList(tstream,17,0);//add '<=' to tokenlist
				pSrc += 2;
			}
			else
			{
				FillTokenList(tstream,16,0);//add '<' to tokenlist
				pSrc++;
			}
			SourceCode = pSrc;
			break;
		case '&'://& and &&
			if (*(pSrc+1) == '&')//	&&
				pSrc += 2;
			else
				pSrc++;
			FillTokenList(tstream,18,0);//add 'AND' to tokenlist
			SourceCode = pSrc;
			break;
		case '|'://| and ||
			if (*(pSrc+1) == '|')//	||
				pSrc += 2;
			else
				pSrc++;
			FillTokenList(tstream,19,0);//add 'OR' to tokenlist
			SourceCode = pSrc;
			break;
		case '+'://+
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,10,0);//<+,NULL>
			break;
		case '-'://-
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,11,0);//<-,NULL>
			break;
		case '*'://*
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,12,0);//<*,NULL>
			break;
		case ';'://;
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,20,0);//<;,NULL>
			break;
		case '('://(
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,22,0);//<(,NULL>
			break;
		case '{'://{
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,23,0);//<{,NULL>
			break;
		case '}'://}
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,24,0);//<},NULL>
			break;
		case ')'://)
			pSrc++;
			SourceCode = pSrc;
			FillTokenList(tstream,25,0);//<),NULL>
			break;
		default:
			while (*SourceCode == ' ' || *SourceCode <= 10)//catch space or enter, go on
				SourceCode++;
			pSrc = SourceCode;
			if (isalnum(*SourceCode))//scan one alpha
			{
				pSrc++;
				while (isalnum(*pSrc))
					pSrc++;//scan char and num in this word
				temp = *pSrc;//save the next char of the word
				*pSrc = 0;//cut down
				wordlabel = CheckWord(SourceCode);
				switch (wordlabel)
				{
				case 0:case 1://declaration
					if (SortAndFill(wordlabel,tstream,&pSrc,temp) == -1)//sort error return NULL
						return NULL;
					break;
				case 2://while
					*pSrc = temp;
					FillTokenList(tstream,2,0);//<while,NULL>
					break;
				case 3://if
					*pSrc = temp;
					FillTokenList(tstream,3,0);//<if,NULL>
					break;
				case 4://else
					*pSrc = temp;
					FillTokenList(tstream,4,0);//<else,NULL>
					break;
				case 5://break
					*pSrc = temp;
					FillTokenList(tstream,5,0);//<break,NULL>
					break;
				case 6://return
					*pSrc = temp;
					FillTokenList(tstream,6,0);//<return,NULL>
					break;
				case -1://number, id, or unknowns
					if (atoi(SourceCode)/strlen(SourceCode))//if all characters are number
					{
						*pSrc = temp;
						FillTokenList(tstream,27,atoi(SourceCode));//<number,value>
					}
					else if (i = FindIntIdExists(SourceCode))//int_id
					{
						*pSrc = temp;
						FillTokenList(tstream,21,i);//<int_id,index>
					}
					else if (i = FindCharIdExists(SourceCode))//char_id
					{
						*pSrc = temp;
						FillTokenList(tstream,28,i);//<char_id,index>
					}
					else
					{
						printf("Unknown id\n");
						return NULL;
					}
					break;
				default:
					break;
				}
				
			}
			if (*pSrc == ';')//fill end of sentence
			{
				pSrc += 2;
				FillTokenList(tstream,20,0);//<;,NULL>
			}
			if (*pSrc == '\n' && !*(pSrc+2))//meet enter
				pSrc += 2;
			SourceCode = pSrc;
			break;
		}

	}
	return tstream;
}
char* FetchSourceCode(char *FilePath)
{/*Input FilePath, if succeed return sourcecode, else return NULL*/
	long file_size;
	char *SourceCodeBuf = NULL;
	FILE *FilePointer = NULL;
	int i;
	FilePointer = fopen(FilePath,"r");
	if (!FilePointer)
		return NULL;//file open failed
	fseek(FilePointer,0,SEEK_END);
	file_size = ftell(FilePointer);//get file size
	SourceCodeBuf = (char*)malloc((file_size+1)*sizeof(char));//allocate buffer
	for (i=0;i<file_size;i++)
		SourceCodeBuf[i] = 0;//file end
	if (!SourceCodeBuf)//allocate failed
	{
		fclose(FilePointer);
		return NULL;
	}
	fseek(FilePointer,0,SEEK_SET);
	fread(SourceCodeBuf,1,file_size,FilePointer);//save source code to buf
	fclose(FilePointer);
	
	return SourceCodeBuf;
}
int OutputList(tokenlist *pList)
{
	long i;
	printf("token:<label,value>\n");
	for (i=0;i<pList->length;i++)
	{
		printf("<%s\t,%d>\n",label_series[pList->list[i].label],pList->list[i].value);
	}
	return 0;
}

int main(int argc, char **argv)
{
	char *SourceCode = NULL;
	tokenlist *pTokenList = NULL;
	if (argc == 2 && argv[1][0])//only one parameter and not NULL, begin
	{
		SourceCode = FetchSourceCode("C:\\Users\\Administrator\\Desktop\\Test\\maybeC\\Debug\\1.txt"/*argv[1]*/);//read file
		if (!SourceCode)
		{
			printf("Fetch source code error!");
			return 2;//fetch failed
		}
		pTokenList = Lexical_Analysis(SourceCode);
		OutputList(pTokenList);
		return 0;
	}
	else//wrong parameters list
	{
		printf("Format: maybeC xxx.c(Your c lang source file path)");//show Tip
		return 1;
	}
}